/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Tonny Madsen, RCP Company - bug 201055 * Lars Vogel <Lars.Vogel@gmail.com> - Bug 440810 *******************************************************************************/ package org.eclipse.ui.actions; import com.ibm.icu.text.Collator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.NotEnabledException; import org.eclipse.core.commands.NotHandledException; import org.eclipse.core.commands.common.NotDefinedException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.ContributionItem; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.ui.IPerspectiveDescriptor; import org.eclipse.ui.IPerspectiveRegistry; import org.eclipse.ui.IWorkbenchCommandConstants; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPreferenceConstants; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.activities.WorkbenchActivityHelper; import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.internal.WorkbenchMessages; import org.eclipse.ui.internal.WorkbenchPlugin; import org.eclipse.ui.internal.util.PrefUtil; import org.eclipse.ui.statushandlers.StatusManager; /** * A menu for perspective selection. * <p> * A <code>PerspectiveMenu</code> is used to populate a menu with * perspective shortcut items. If the user selects one of these items * an action is performed for the selected perspective. * </p><p> * The visible perspective items within the menu are dynamic and reflect the * available set generated by each subclass. The default available set consists * of the perspective shortcut list of the current perspective. * </p><p> * This class is abstract. Subclasses must implement the <code>run</code> method, * which performs a specialized action for the selected perspective. * </p> */ public abstract class PerspectiveMenu extends ContributionItem { /** * @since 3.4 * @deprecated As of 3.5, replaced by {@link IWorkbenchCommandConstants#PERSPECTIVES_SHOW_PERSPECTIVE} */ @Deprecated protected static final String SHOW_PERSP_ID = IWorkbenchCommandConstants.PERSPECTIVES_SHOW_PERSPECTIVE; private IPerspectiveRegistry reg; private IWorkbenchWindow window; private boolean showActive = false; private boolean dirty = true; private IMenuListener menuListener = manager -> { manager.markDirty(); dirty = true; }; private Comparator comparator = new Comparator() { private Collator collator = Collator.getInstance(); @Override public int compare(Object ob1, Object ob2) { IPerspectiveDescriptor d1 = (IPerspectiveDescriptor) ob1; IPerspectiveDescriptor d2 = (IPerspectiveDescriptor) ob2; return collator.compare(d1.getLabel(), d2.getLabel()); } }; /** * The translatable message to show when there are no perspectives. * * @since 3.1 */ private static final String NO_TARGETS_MSG = WorkbenchMessages.Workbench_showInNoPerspectives; /** * The map of perspective identifiers (String) to actions * (OpenPerspectiveAction). This map may be empty, but it is never * <code>null</code>. * * @since 3.1 */ private Map actions = new HashMap(); /** * The action for that allows the user to choose any perspective to open. * * @since 3.1 */ private Action openOtherAction = new Action(WorkbenchMessages.PerspectiveMenu_otherItem) { @Override public final void runWithEvent(final Event event) { runOther(new SelectionEvent(event)); } }; /** * Constructs a new instance of <code>PerspectiveMenu</code>. * * @param window the window containing this menu * @param id the menu id */ public PerspectiveMenu(IWorkbenchWindow window, String id) { super(id); this.window = window; reg = window.getWorkbench().getPerspectiveRegistry(); openOtherAction .setActionDefinitionId(IWorkbenchCommandConstants.PERSPECTIVES_SHOW_PERSPECTIVE); } @Override public void fill(Menu menu, int index) { if (getParent() instanceof MenuManager) { ((MenuManager) getParent()).addMenuListener(menuListener); } if (!dirty) { return; } final MenuManager manager = new MenuManager(); fillMenu(manager); final IContributionItem items[] = manager.getItems(); if (items.length == 0) { MenuItem item = new MenuItem(menu, SWT.NONE, index++); item.setText(NO_TARGETS_MSG); item.setEnabled(false); } else { for (IContributionItem item : items) { item.fill(menu, index++); } } dirty = false; } /** * Fills the given menu manager with all the open perspective actions * appropriate for the currently active perspective. Filtering is applied to * the actions based on the activities and capabilities mechanism. * * @param manager * The menu manager that should receive the menu items; must not * be <code>null</code>. * @since 3.1 */ private final void fillMenu(final MenuManager manager) { // Clear out the manager so that we have a blank slate. manager.removeAll(); // Collect and sort perspective descriptors. final List persps = getPerspectiveItems(); Collections.sort(persps, comparator); /* * Convert the perspective descriptors to actions, and filter out * actions using the activity/capability mechanism. */ final List actions = new ArrayList(persps.size()); for (Iterator i = persps.iterator(); i.hasNext();) { final IPerspectiveDescriptor descriptor = (IPerspectiveDescriptor) i .next(); final IAction action = getAction(descriptor.getId()); if (action != null) { if (WorkbenchActivityHelper.filterItem(action)) { continue; } actions.add(action); } } // Go through and add each of the actions to the menu manager. for (Iterator i = actions.iterator(); i.hasNext();) { manager.add((IAction) i.next()); } if (PrefUtil .getAPIPreferenceStore() .getBoolean( IWorkbenchPreferenceConstants.SHOW_OTHER_IN_PERSPECTIVE_MENU)) { // Add a separator and then "Other..." if (actions.size() > 0) { manager.add(new Separator()); } manager.add(openOtherAction); } } /** * Returns the action for the given perspective id. This is a lazy cache. If * the action does not already exist, then it is created. If there is no * perspective with the given identifier, then the action is not created. * * @param id * The identifier of the perspective for which the action should * be retrieved. * @return The action for the given identifier; or <code>null</code> if * there is no perspective with the given identifier. * @since 3.1 */ private final IAction getAction(final String id) { IAction action = (IAction) actions.get(id); if (action == null) { IPerspectiveDescriptor descriptor = reg.findPerspectiveWithId(id); if (descriptor != null) { action = new OpenPerspectiveAction(window, descriptor, this); action.setActionDefinitionId(id); actions.put(id, action); } } return action; } /* * Returns the perspective shortcut items for the active perspective. * * @return a list of <code>IPerspectiveDescriptor</code> items */ private ArrayList getPerspectiveShortcuts() { ArrayList list = new ArrayList(); IWorkbenchPage page = window.getActivePage(); if (page == null) { return list; } String[] ids = page.getPerspectiveShortcuts(); for (String perspectiveId : ids) { IPerspectiveDescriptor desc = reg.findPerspectiveWithId(perspectiveId); if (desc != null && !list.contains(desc)) { if (WorkbenchActivityHelper.filterItem(desc)) { continue; } list.add(desc); } } return list; } /** * Returns the available list of perspectives to display in the menu. * <p> * By default, the list contains the perspective shortcuts * for the current perspective. * </p><p> * Subclasses can override this method to return a different list. * </p> * * @return an <code>ArrayList<code> of perspective items <code>IPerspectiveDescriptor</code> */ protected ArrayList getPerspectiveItems() { /* Allow the user to see all the perspectives they have * selected via Customize Perspective. Bugzilla bug #23445 */ ArrayList shortcuts = getPerspectiveShortcuts(); ArrayList list = new ArrayList(shortcuts.size()); // Add perspective shortcuts from the active perspective int size = shortcuts.size(); for (int i = 0; i < size; i++) { if (!list.contains(shortcuts.get(i))) { list.add(shortcuts.get(i)); } } return list; } /** * Returns whether the menu item representing the active perspective * will have a check mark. * * @return <code>true</code> if a check mark is shown, <code>false</code> otherwise */ protected boolean getShowActive() { return showActive; } /** * Returns the window for this menu. * * @return the window */ protected IWorkbenchWindow getWindow() { return window; } @Override public boolean isDirty() { return dirty; } @Override public boolean isDynamic() { return true; } /** * Runs an action for a particular perspective. The behavior of the * action is defined by the subclass. * * @param desc the selected perspective */ protected abstract void run(IPerspectiveDescriptor desc); /** * Runs an action for a particular perspective. The behavior of the action * is defined by the subclass. By default, this just calls * <code>run(IPerspectiveDescriptor)</code>. * * @param desc * the selected perspective * @param event * SelectionEvent - the event send along with the selection * callback */ protected void run(IPerspectiveDescriptor desc, SelectionEvent event) { //Do a run without the event by default run(desc); } /** * Show the "other" dialog, select a perspective, and run it. Pass on the selection event should * the menu need it. * * @param event the selection event */ void runOther(SelectionEvent event) { IHandlerService handlerService = window .getService(IHandlerService.class); try { handlerService.executeCommand(IWorkbenchCommandConstants.PERSPECTIVES_SHOW_PERSPECTIVE, null); } catch (ExecutionException e) { StatusManager.getManager().handle( new Status(IStatus.WARNING, WorkbenchPlugin.PI_WORKBENCH, "Failed to execute " + IWorkbenchCommandConstants.PERSPECTIVES_SHOW_PERSPECTIVE, e)); //$NON-NLS-1$ } catch (NotDefinedException e) { StatusManager.getManager().handle( new Status(IStatus.WARNING, WorkbenchPlugin.PI_WORKBENCH, "Failed to execute " + IWorkbenchCommandConstants.PERSPECTIVES_SHOW_PERSPECTIVE, e)); //$NON-NLS-1$ } catch (NotEnabledException e) { StatusManager.getManager().handle( new Status(IStatus.WARNING, WorkbenchPlugin.PI_WORKBENCH, "Failed to execute " + IWorkbenchCommandConstants.PERSPECTIVES_SHOW_PERSPECTIVE, e)); //$NON-NLS-1$ } catch (NotHandledException e) { StatusManager.getManager().handle( new Status(IStatus.WARNING, WorkbenchPlugin.PI_WORKBENCH, "Failed to execute " + IWorkbenchCommandConstants.PERSPECTIVES_SHOW_PERSPECTIVE, e)); //$NON-NLS-1$ } } /** * Sets the showActive flag. If <code>showActive == true</code> then the * active perspective is hilighted with a check mark. * * @param b the new showActive flag */ protected void showActive(boolean b) { showActive = b; } }